package com.feinno.urcs.group.service;

import com.feinno.database.DataTable;
import com.feinno.database.Database;
import com.feinno.urcs.content.MimeType;
import com.feinno.urcs.group.api.GroupMessageStorageService;
import com.feinno.urcs.group.api.GroupOfflineMessageService;
import com.feinno.urcs.group.entity.*;
import com.feinno.urcs.nai.IMPU;
import com.feinno.urcs.profile.entity.ClientType;
import com.feinno.urcs.storage.MessageStorageService;
import com.feinno.urcs.storage.entity.MessageSendStatus;
import com.feinno.urcs.storage.entity.MessageType;
import com.feinno.urcs.storage.entity.TextMessage;
import com.feinno.urcs.storage.entity.TextStorage;
import com.feinno.urcs.types.MobileNo;
import org.helium.framework.annotations.FieldSetter;
import org.helium.framework.annotations.ServiceImplementation;
import org.helium.framework.annotations.ServiceSetter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.sql.SQLException;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author Li.Hongbo <lihongbo@feinno.com>
 */
@ServiceImplementation
public class GroupOfflineMessageServiceImpl implements GroupOfflineMessageService {

	@FieldSetter("${RMSDB}")
	private Database rmsdb;
	@ServiceSetter
	private GroupMessageStorageService groupMessageStorageService;

	@ServiceSetter
	private MessageStorageService messageStorageService;

	private static final Logger LOGGER = LoggerFactory.getLogger(GroupOfflineMessageServiceImpl.class);

	/**
	 * 如果存在离线消息的索引, 则什么也不用干了. 理论上 新的离线消息id 比已经存在的大.
	 * 如果不存在消息的索引,则需要创建一个messagebase, 存起来. 然后创建一个离线消息的索引 储存起来.
	 *
	 * @param receiver       接受者
	 * @param groupId        群id
	 * @param groupMessageId 群消息的id , 来自于消息持久化服务, 仅用作消息的排序,与离线偏移量存储, 不对客户端下行.
	 */
	@Override
	public void saveOfflineTextMessage(MobileNo receiver, int groupId, long groupMessageId) {


		GroupOfflineMessageIndex messageIndex = geOfflineMessageIndex(receiver, groupId, GroupOfflineMessageType.TEXT);
		if (messageIndex == null) {
			try {
				TextMessage textMessage = messageStorageService.createTextMessage();
				textMessage.setMessageFrom(IMPU.parse(GroupIndex.toConferenceUri(groupId)));
				textMessage.setMessageTo(IMPU.fromUri(receiver.toTelUri()));
				textMessage.setMessageSender(IMPU.parse(GroupIndex.toConferenceUri(groupId)));
				textMessage.setMessageReceiver(IMPU.fromUri(receiver.toTelUri()));
				textMessage.setMessageCreateTime(new Date());
				textMessage.setMessageType(MessageType.BASE_MESSAGE);
				textMessage.setMessageClientType(ClientType.APP);
//                textMessage.getMessageData().setMessageBarcycle(false);
//                textMessage.getMessageData().setTextContentType(MimeType.TextPlain);
//                textMessage.getMessageData().setMessageContentType(MimeType.TextPlain);
//                textMessage.getMessageData().setTextContent(new byte[0]);
				//TODO set XXXXX
				messageStorageService.setMessageBase(textMessage);

				messageIndex = new GroupOfflineMessageIndex();
				messageIndex.setMessageType(GroupOfflineMessageType.TEXT);
				messageIndex.setGroupMessageStorageId(groupMessageId);
				messageIndex.setMessageBaseId(textMessage.getMessageId());
				messageIndex.setGroupId(groupId);
				messageIndex.setReceiver(receiver);
				long line = saveOfflineMessage(messageIndex);
				// TODO 验证是否成功...
				LOGGER.info(String.format("saveOfflineTextMessage saveEnd 111 receiver=%s, groupId=%s, groupMessageId=%s line=%s ", receiver, groupId, groupMessageId, line));

				if (line != 1) {
					LOGGER.info(String.format("saveOfflineTextMessage saveEnd 111 line=%s try again receiver=%s, groupId=%s, groupMessageId=%s ", line, receiver, groupId, groupMessageId));

					try {
						line = saveOfflineMessage(messageIndex);
					} catch (Exception ee) {
						LOGGER.warn(String.format("saveOfflineTextMessage saveEnd error 333 receiver=%s, groupId=%s, groupMessageId=%s line=%s messageIndex=%s ", receiver, groupId, groupMessageId, line, messageIndex), ee);
					}
				}
				if (line != 1) {
					LOGGER.error(String.format("saveOfflineTextMessage saveEnd 222 receiver=%s, groupId=%s, groupMessageId=%s line=%s messageIndex=%s ", receiver, groupId, groupMessageId, line, messageIndex));
				}

			} catch (Exception e) {
				LOGGER.error(String.format("saveOfflineTextMessage Error receiver=%s, groupId=%s, groupMessageId=%s",
						receiver, groupId, groupMessageId), e);
			}

		} else {
			LOGGER.info(String.format("hasOfflineMessage messageIndex exist receiver=%s,groupId=%s,groupMessageId=%s ,messageIndex=%s",
					receiver, groupId, groupMessageId, messageIndex.toJsonObject().toString()));
		}

	}

	private long updateOfflineMessageStorageId(GroupOfflineMessageIndex messageIndex) throws SQLException {
		try {
			String sql = "update group_offline_message_index set group_message_id=? " +
					"where receiver=? and group_id= ? and message_type=? ;";

			return rmsdb.executeNonQuery(sql,
					messageIndex.getGroupMessageStorageId(),
					messageIndex.getReceiver().toString(),
					messageIndex.getGroupId(),
					messageIndex.getMessageType().intValue());
		} catch (Exception e) {
			LOGGER.error("create groupMessageIndex error args=" + messageIndex.toJsonObject().toString(), e);
			throw e;
		}

	}

	private long saveOfflineMessage(GroupOfflineMessageIndex messageIndex) throws SQLException {
		try {
			String sql = "insert into group_offline_message_index( receiver, group_id , message_base_id, group_message_id, message_type )   values(?,?,?,?,?);";
			return rmsdb.executeNonQuery(sql,
					messageIndex.getReceiver().toString(),
					messageIndex.getGroupId(),
					messageIndex.getMessageBaseId(),
					messageIndex.getGroupMessageStorageId(),
					messageIndex.getMessageType().intValue());
		} catch (Exception e) {
			LOGGER.error("saveOfflineMessage error args=%s" + messageIndex.toJsonObject().toString(), e);
			throw e;
		}
	}


	public GroupOfflineMessageIndex getOfflineMessageIndexByMessageBaseId(String messageBaseId) {

		String sql = "select receiver, group_id, message_base_id, group_message_id, message_type" +
				" from group_offline_message_index where message_base_id=? ;";
		GroupOfflineMessageIndex offlineMessageIndex = null;
		try {
			DataTable dt = rmsdb.executeTable(sql, messageBaseId);
			if (dt.getRows() != null && dt.getRows().size() > 0) {
				offlineMessageIndex = new GroupOfflineMessageIndex(dt.getRow(0));
			}
		} catch (Exception e) {
			LOGGER.error(String.format("getOfflineMessageIndexByMessageBaseId messageBaseId=%s", messageBaseId), e);

		}
		return offlineMessageIndex;
	}

	/**
	 * 保存离线的邀请, 一个人一个群只保存一个就行,不管其他人的重复邀请了.
	 *
	 * @param receiver 接受者
	 * @param groupId  群id
	 * @param referBy  发起者
	 */
	@Override
	public void saveInvite(MobileNo receiver, int groupId, MobileNo referBy) {

		GroupOfflineMessageIndex messageIndex = geOfflineMessageIndex(receiver, groupId, GroupOfflineMessageType.INVITE);
		if (messageIndex == null) {
			try {
				TextMessage textMessage = messageStorageService.createTextMessage();
				textMessage.setMessageFrom(IMPU.parse(GroupIndex.toConferenceUri(groupId)));
				textMessage.setMessageTo(IMPU.fromUri(receiver.toTelUri()));
				textMessage.setMessageSender(IMPU.parse(GroupIndex.toConferenceUri(groupId)));
				textMessage.setMessageReferBy(referBy.toString());
				textMessage.setMessageReceiver(IMPU.fromUri(receiver.toTelUri()));

				textMessage.setMessageCreateTime(new Date());

				//TODO set XXXXX
				textMessage.setMessageType(MessageType.BASE_MESSAGE);
				textMessage.setMessageClientType(ClientType.APP);
//                textMessage.getMessageData().setMessageBarcycle(false);
//                textMessage.getMessageData().setTextContentType(MimeType.TextPlain);
//                textMessage.getMessageData().setMessageContentType(MimeType.TextPlain);
//                textMessage.getMessageData().setTextContent(new byte[0]);

				messageStorageService.setMessageBase(textMessage);

				messageIndex = new GroupOfflineMessageIndex();
				messageIndex.setMessageType(GroupOfflineMessageType.INVITE);
				messageIndex.setGroupMessageStorageId(-1); // 这个不需要GroupMessageStorageId
				messageIndex.setMessageBaseId(textMessage.getMessageId());
				messageIndex.setGroupId(groupId);
				messageIndex.setReceiver(receiver);
				long line = saveOfflineMessage(messageIndex);
				// TODO 验证是否成功...
				LOGGER.info(String.format("saveOfflineINVITEMessage saveEnd receiver=%s, groupId=%s,  line=%s ", receiver, groupId, line));
				if (line != 1) {
					LOGGER.error(String.format("saveOfflineINVITEMessage saveEnd receiver=%s, groupId=%s,  line=%s messageIndex=%s ", receiver, groupId, line, messageIndex));
				}


			} catch (Exception e) {
				LOGGER.error(String.format("saveInvite error receiver=%s, groupId=%s, referBy=%s",
						receiver, groupId, referBy), e);
			}

		} else {
			LOGGER.info(String.format("saveInvite receiver=%s, groupId=%s, messageIndex=%s",
					receiver, groupId, messageIndex.toJsonObject().toString()));
		}
	}

	/**
	 * 这个发送失败了. 需要干两件事情, 一个是确认MessageBase 中的记录存在并且为offline 状态;
	 * 另一个是确定index 的偏移量正确,暂时认为偏移量 小于等于此index 的值为正确吧(这么干可能会导致重复收到某消息)
	 *
	 * @param offlineMessageIndex
	 */
	public void sendFail(GroupOfflineMessageIndex offlineMessageIndex) {

		if (offlineMessageIndex != null) {
			try {
				messageStorageService.updateMessageStatus(offlineMessageIndex.getMessageBaseId(), MessageSendStatus.MESSAGE_INIT);
				GroupOfflineMessageIndex dbIndex = geOfflineMessageIndex(offlineMessageIndex.getReceiver(),
						offlineMessageIndex.getGroupId(), offlineMessageIndex.getMessageType());

				if (null == dbIndex) {
					saveOfflineMessage(offlineMessageIndex);
					return;
				}

				if (offlineMessageIndex.getMessageType().equals(GroupOfflineMessageType.TEXT)) {
					if (dbIndex.getGroupMessageStorageId() > offlineMessageIndex.getGroupMessageStorageId()) {
						offlineMessageIndex.setGroupMessageStorageId(dbIndex.getGroupMessageStorageId());
						updateOfflineMessageStorageId(offlineMessageIndex);
					}
				}

			} catch (Exception e) {
				e.printStackTrace();
			}
		}

	}

	@Override
	public List<GroupTextOfflineMessage> getOfflineTextMessage(MobileNo receiver, int groupId, int maxSize) {
		GroupOfflineMessageIndex messageIndex = geOfflineMessageIndex(receiver, groupId, GroupOfflineMessageType.TEXT);

		List<GroupTextOfflineMessage> result = null;
		if (messageIndex != null) {
			try {
				Collection<GroupTextMessageStorageEntity> messages = groupMessageStorageService.getTextMessage(groupId, messageIndex.getGroupMessageStorageId(), maxSize);

				result = messages.stream().map(storageEntity -> {
							return new GroupTextOfflineMessage(receiver, groupId, messageIndex, storageEntity);
						}
				).collect(Collectors.toList());
			} catch (Exception e) {
				LOGGER.error(String.format("getOfflineTextMessage error receiver=%s,groupId=%s,maxSize=%s,", receiver, groupId, maxSize), e);
			}
		}
		return result;
	}

	/**
	 * 是否还有离线消息,
	 *
	 * @param receiver
	 * @param groupId
	 * @return 还有离线消息=true
	 */
	@Override
	public boolean hasOfflineMessage(MobileNo receiver, int groupId, GroupOfflineMessageType type) {

		String sql = "select receiver, group_id , message_base_id, group_message_id, message_type from group_offline_message_index where group_id=? and receiver=? and message_type=?";
		try {
			DataTable dt = rmsdb.executeTable(sql,
					groupId,
					receiver.toString(),
					type.intValue()
			);
			if (dt.getRows() != null && dt.getRows().size() > 0) {
				long group_message_id = dt.getRow(0).getLong("group_message_id");
				if (group_message_id > 0) {
					boolean hasGroupMessage = groupMessageStorageService.hasGroupMessage(groupId, group_message_id);
					if (hasGroupMessage) return true;
				}

				//TODO 没有更多的消息了. 清理离线相关的东西.包含messagebase 表, 群离线索引表
				cleanOfflineMessageIndex(receiver, groupId, type);
			}
			return false;
		} catch (Exception e) {
			LOGGER.error(String.format("hasOfflineMessage error receiver=%s,groupId=%s", receiver, groupId), e);
			return true; // 出异常了, 当做有离线吧. 让他尝试去发送离线.
		}

	}

	private GroupOfflineMessageIndex geOfflineMessageIndex(MobileNo receiver, int groupId, GroupOfflineMessageType type) {

		String sql = "select receiver, group_id , message_base_id, group_message_id, message_type from group_offline_message_index where group_id=? and receiver=? and message_type=?";
		GroupOfflineMessageIndex offlineMessageIndex = null;
		try {
			DataTable dt = rmsdb.executeTable(sql,
					groupId,
					receiver.toString(),
					type.intValue()
			);
			if (dt.getRows() != null && dt.getRows().size() > 0) {
				offlineMessageIndex = new GroupOfflineMessageIndex(dt.getRow(0));
			}
		} catch (Exception e) {
			LOGGER.error(String.format("hasOfflineMessage error receiver=%s,groupId=%s,type=%s", receiver, groupId, type.intValue()), e);

		}
		return offlineMessageIndex;
	}

	@Override
	public void deliveredTextMessage(MobileNo memberMobile, int groupId, GroupTextOfflineMessage offlineMessage) {
		String sql = "update group_offline_message_index set group_message_id=? where group_id=? and receiver=? and message_type=?";

		try {
			int row = rmsdb.executeNonQuery(sql,
					offlineMessage.getGroupMessageStorageId() + 1,
					groupId,
					memberMobile.toString(),
					GroupOfflineMessageType.TEXT.intValue()
			);

			LOGGER.info(String.format("delivered message  memberMobile=%s,groupId=%s,offlineMessageGroupId=%s",
					memberMobile,groupId, offlineMessage.getGroupMessageStorageId() + 1));
			if (row != 1) {
				LOGGER.error(String.format("delivered message error receiver=%s,groupId=%s,offlineMessage=%s",
						memberMobile, groupId, offlineMessage.toJsonObject().toString()));
			}
		} catch (Exception e) {
			LOGGER.error(String.format("delivered message error receiver=%s,groupId=%s,offlineMessage=%s",
					memberMobile, groupId, offlineMessage.toJsonObject().toString()), e);
		}
	}

	@Override
	public void deliveredInvite(GroupOfflineMessageIndex messageIndex) {
		deliveredInvite(messageIndex.getReceiver(), messageIndex.getGroupId());
	}

	@Override
	public void deliveredInvite(MobileNo memberMobile, int groupId) {


		String sql = "delete from group_offline_message_index  where group_id=? and receiver=? and message_type=? ";
		try {
			GroupOfflineMessageIndex messageIndex = geOfflineMessageIndex(memberMobile, groupId, GroupOfflineMessageType.INVITE);
			if (messageIndex != null) {
				messageStorageService.deleteMessage(messageIndex.getMessageBaseId());
			}

			int row = rmsdb.executeNonQuery(sql,
					groupId, memberMobile.toString(), GroupOfflineMessageType.INVITE.intValue());
			LOGGER.info(String.format("delivered INVITE error receiver=%s,groupId=%s",
					memberMobile,groupId));
			if (row != 1) {
				LOGGER.error(String.format("deliveredInvite error receiver=%s,groupId=%s", memberMobile, groupId));
			}
		} catch (Exception e) {
			LOGGER.error(String.format("deliveredInvite error receiver=%s,groupId=%s", memberMobile, groupId), e);
		}
	}


	private void deleteTextMessageIndex(MobileNo memberMobile, int groupId) {
		String sql = "delete from group_offline_message_index  where group_id=? and receiver=? and message_type=? ";
		try {
			GroupOfflineMessageIndex messageIndex = geOfflineMessageIndex(memberMobile, groupId, GroupOfflineMessageType.TEXT);
			if (messageIndex != null) {
				messageStorageService.deleteMessage(messageIndex.getMessageBaseId());
			}
			int row = rmsdb.executeNonQuery(sql,
					groupId,
					memberMobile.toString(),
					GroupOfflineMessageType.TEXT.intValue()
			);
			LOGGER.info(String.format("deleteTextMessageIndex  memberMobile=%s,groupId=%s row=%s",
					memberMobile,groupId,row));
			if (row != 1) {
				LOGGER.error(String.format("deleteTextMessageIndex error receiver=%s,groupId=%s", memberMobile, groupId));
			}
		} catch (Exception e) {
			LOGGER.error(String.format("error receiver=%s,groupId=%s", memberMobile, groupId), e);
		}
	}

	@Override
	public void cleanOfflineMessageIndex(MobileNo memberMobile, int groupId, GroupOfflineMessageType type) {
		try {
			deleteTextMessageIndex(memberMobile, groupId);
			//TODO impl
		} catch (Exception e) {
			LOGGER.error(String.format("cleanOfflineMessageIndex memberMobile=%s,groupId=%s type=%s", memberMobile, groupId,type), e);

		}

	}

}
